package org.convallaria.infrastruct.ce.config;

import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.convallaria.infrastruct.ce.cache.HybridCacheManager;
import org.convallaria.infrastruct.ce.properties.CacheProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.HashMap;
import java.util.Map;

/**
 * 企业级缓存配置类
 * 支持多级缓存、监控、细粒度配置
 * 
 * @author gjh
 * @since 2024-09-24
 */
@Slf4j
@Configuration
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class)
@ConditionalOnProperty(prefix = "convallaria.cache", name = "enabled", havingValue = "true", matchIfMissing = true)
public class EnterpriseCacheConfig {

    private final CacheProperties cacheProperties;

    public EnterpriseCacheConfig(CacheProperties cacheProperties) {
        this.cacheProperties = cacheProperties;
    }

    /**
     * Redis缓存管理器
     * 
     * 配置Redis作为分布式缓存存储，支持：
     * - 自定义TTL过期时间
     * - JSON序列化存储
     * - 空值缓存控制
     * - 多缓存实例配置
     * 
     * @param connectionFactory Redis连接工厂
     * @return Redis缓存管理器实例
     */
    @Bean
    @Primary
    @ConditionalOnClass(RedisConnectionFactory.class)
    @ConditionalOnProperty(prefix = "convallaria.cache", name = "type", havingValue = "redis", matchIfMissing = false)
    public CacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
        log.info("初始化Redis缓存管理器 - 支持分布式缓存和JSON序列化");
        
        // 使用非锁定写入器，提高并发性能
        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
        
        // 默认缓存配置
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration
                .defaultCacheConfig()
                .entryTtl(cacheProperties.getDefaultConfig().getTtl())
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues();

        // 特定缓存配置 - 支持不同缓存实例的个性化配置
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        cacheProperties.getCaches().forEach((cacheName, config) -> {
            RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(config.getTtl() != null ? config.getTtl() : cacheProperties.getDefaultConfig().getTtl())
                    .serializeKeysWith(RedisSerializationContext.SerializationPair
                            .fromSerializer(new StringRedisSerializer()))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair
                            .fromSerializer(new GenericJackson2JsonRedisSerializer()));
            
            // 根据配置决定是否允许空值缓存
            if (Boolean.FALSE.equals(config.getAllowNullValues())) {
                cacheConfig = cacheConfig.disableCachingNullValues();
            }
            
            cacheConfigurations.put(cacheName, cacheConfig);
            log.debug("配置缓存实例: {} - TTL: {}, 允许空值: {}", 
                cacheName, config.getTtl(), config.getAllowNullValues());
        });

        return RedisCacheManager.builder(cacheWriter)
                .cacheDefaults(defaultConfig)
                .withInitialCacheConfigurations(cacheConfigurations)
                .build();
    }

    /**
     * Caffeine缓存管理器
     */
    @Bean
    @Primary
    @ConditionalOnClass(Caffeine.class)
    @ConditionalOnProperty(prefix = "convallaria.cache", name = "type", havingValue = "caffeine", matchIfMissing = false)
    public CacheManager caffeineCacheManager() {
        log.info("初始化Caffeine缓存管理器");
        
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        
        // 设置默认配置
        Caffeine<Object, Object> defaultCaffeine = Caffeine.newBuilder()
                .maximumSize(cacheProperties.getCaffeine().getDefaultMaximumSize())
                .expireAfterWrite(cacheProperties.getCaffeine().getDefaultTtl());
                // .refreshAfterWrite(cacheProperties.getCaffeine().getRefreshAfterWrite()); // 暂时注释掉，需要LoadingCache
        
        if (cacheProperties.getCaffeine().isEnableStatistics()) {
            defaultCaffeine.recordStats();
        }
        
        cacheManager.setCaffeine(defaultCaffeine);
        
        // 设置特定缓存配置
        Map<String, Caffeine<Object, Object>> cacheSpecs = new HashMap<>();
        cacheProperties.getCaches().forEach((cacheName, config) -> {
            Caffeine<Object, Object> caffeine = Caffeine.newBuilder();
            
            if (config.getMaximumSize() != null) {
                caffeine.maximumSize(config.getMaximumSize());
            } else {
                caffeine.maximumSize(cacheProperties.getCaffeine().getDefaultMaximumSize());
            }
            
            if (config.getTtl() != null) {
                caffeine.expireAfterWrite(config.getTtl());
            } else {
                caffeine.expireAfterWrite(cacheProperties.getCaffeine().getDefaultTtl());
            }
            
            if (Boolean.TRUE.equals(config.getEnableStatistics()) || 
                (config.getEnableStatistics() == null && cacheProperties.getCaffeine().isEnableStatistics())) {
                caffeine.recordStats();
            }
            
            cacheSpecs.put(cacheName, caffeine);
        });
        
        // 注意：CaffeineCacheManager没有setCaffeineSpecs方法，这里需要其他方式配置
        // 可以通过自定义CacheManager或使用其他方式实现
        
        return cacheManager;
    }

    /**
     * 混合缓存管理器（Redis + Caffeine）
     */
    @Bean
    @Primary
    @ConditionalOnClass({RedisConnectionFactory.class, Caffeine.class})
    @ConditionalOnProperty(prefix = "convallaria.cache", name = "type", havingValue = "hybrid", matchIfMissing = false)
    public CacheManager hybridCacheManager(RedisConnectionFactory connectionFactory) {
        log.info("初始化混合缓存管理器（Redis + Caffeine）");
        
        // L1: Caffeine本地缓存
        CaffeineCacheManager l1CacheManager = new CaffeineCacheManager();
        l1CacheManager.setCaffeine(Caffeine.newBuilder()
                .maximumSize(cacheProperties.getCaffeine().getDefaultMaximumSize())
                .expireAfterWrite(cacheProperties.getCaffeine().getDefaultTtl())
                // .refreshAfterWrite(cacheProperties.getCaffeine().getRefreshAfterWrite()) // 暂时注释掉，需要LoadingCache
                .recordStats());
        
        // L2: Redis分布式缓存
        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
        
        // 默认配置
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration
                .defaultCacheConfig()
                .entryTtl(cacheProperties.getDefaultConfig().getTtl())
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues();

        // 特定缓存配置
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        cacheProperties.getCaches().forEach((cacheName, config) -> {
            RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(config.getTtl() != null ? config.getTtl() : cacheProperties.getDefaultConfig().getTtl())
                    .serializeKeysWith(RedisSerializationContext.SerializationPair
                            .fromSerializer(new StringRedisSerializer()))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair
                            .fromSerializer(new GenericJackson2JsonRedisSerializer()));
            
            if (Boolean.FALSE.equals(config.getAllowNullValues())) {
                cacheConfig = cacheConfig.disableCachingNullValues();
            }
            
            cacheConfigurations.put(cacheName, cacheConfig);
        });

        RedisCacheManager l2CacheManager = RedisCacheManager.builder(cacheWriter)
                .cacheDefaults(defaultConfig)
                .withInitialCacheConfigurations(cacheConfigurations)
                .build();
        
        // 创建混合缓存管理器
        return new HybridCacheManager(l1CacheManager, l2CacheManager);
    }

    // CacheMonitoringConfig 现在通过 @Configuration 注解自动配置
    // 不需要在这里手动定义Bean
}
